package com.framework.workflow.service.impl;

import com.framework.common.config.SessionUtil;
import com.framework.common.exception.BusinessException;
import com.framework.common.model.UserInfo;
import com.framework.common.util.AssertUtils;
import com.framework.workflow.cmd.JumpAnyWhereCmd;
import com.framework.workflow.constant.DataDict;
import com.framework.workflow.dto.NodeProperty;
import com.framework.workflow.dto.NodeUser;
import com.framework.workflow.model.WfLog;
import com.framework.workflow.model.WfProcessInstance;
import com.framework.workflow.params.CompleteParam;
import com.framework.workflow.params.NodeSettingParam;
import com.framework.workflow.params.StartParam;
import com.framework.workflow.service.IWfLogService;
import com.framework.workflow.service.IWorkflowService;
import com.framework.workflow.util.VariableUtil;
import com.framework.workflow.util.WorkflowHelper;
import com.framework.workflow.vo.NodeSetting;
import com.framework.workflow.vo.WfResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.TaskServiceImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zzg
 * @date 2025-6-9 9:54
 */
@Slf4j
@Service
public class WorkflowService implements IWorkflowService {
    @Autowired
    private ProcessInstanceService processInstanceService;
    @Autowired
    private VariableService variableService;
    @Autowired
    private PropertyService propertyService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private IWfLogService wfLogService;

    @Override
    public WfResult startProcess(StartParam startParam) {
        AssertUtils.notNull(startParam.getBusinessId(), "业务编号");
        AssertUtils.notNull(startParam.getProcDefKey(), "流程定义");

        Map<String, Object> variables = startParam.getAttribute();
        List<String> users = startParam.getSelectAssigneeList();
        // 成功返回值
        WfResult result = new WfResult();

        log.info("启动流程开始:businessId={};defKey={}", startParam.getBusinessId(), startParam.getProcDefKey());

        // 设置参数
        WfProcessInstance ins = setWfProcessInstance(startParam);
        // 不在entity中，额外的业务参数
        Map<String, Object> processVariables = new HashMap<>(16);
        // 获取下一个节点信息
        ProcessDefinition processDefinition = propertyService.getProcess(startParam.getProcDefKey());

        //  设置流程定义ID
        ins.setProcDefId(processDefinition.getId());

        // 设置下一节点处理人
        NodeProperty nodeProperty = propertyService.getNextNodeProperty(processDefinition.getId(), null, variables);
        if (ObjectUtils.isEmpty(users)) {
            NodeSettingParam nodeSettingParam = new NodeSettingParam();
            nodeSettingParam.setProcDefKey(startParam.getProcDefKey());
            NodeSetting setting = propertyService.getNodeSetting(nodeSettingParam);
            users = setting.getNextUserList().stream().map(NodeUser::getId).collect(Collectors.toList());
        }

        // 下一节点处理人存入变量中
        VariableUtil.setVariables(nodeProperty, processVariables, users);
        // 设置通用参数
        VariableUtil.setStartVariables(processVariables, startParam);

        ProcessInstance instance;
        try {
            // 启动流程实例
            instance = runtimeService.startProcessInstanceByKey(startParam.getProcDefKey(), startParam.getBusinessId(), processVariables);
        } catch (Exception e) {
            throw new BusinessException("流程启动失败，请联系管理员", e);
        }

        ins.setProcInstId(instance.getId());
        // 返回对象赋值流程实例ID
        ins.setStatus(DataDict.WfInsStatus.TO_START.toString());

        // 保存流程实例
        processInstanceService.save(ins);

        result.setStatus(DataDict.WfInsStatus.TO_START.toString());
        if (startParam.isAutoCompleteFirstTask()) {
            // 开始节点自动通过
            CompleteParam firstTask = WorkflowHelper.getStartModel(startParam);
            firstTask.setProcInstId(instance.getId());
            result = completeTask(firstTask);
        }

        result.setProcInsId(instance.getId());
        result.setGroups(nodeProperty.getGroups());

        log.info("启动流程结束...");
        return result;
    }

    /**
     * @param param 审批参数
     * @return 处理结果
     */
    @Override
    public WfResult completeTask(CompleteParam param) {
        log.info("处理任务开始: instanceId={}", param.getProcInstId());

        // 获取当前任务
        Task currentTask = getTask(param.getProcInstId());

        // 初始化流程状态和变量
        WfProcessInstance processInstance = processInstanceService.findByProcInsId(currentTask.getProcessInstanceId());
        Map<String, Object> processVariables = variableService.getVariable(currentTask.getProcessInstanceId());

        // 设置通用流程变量
        VariableUtil.setCompleteVariables(processVariables, param);

        // 获取下一节点信息
        NodeProperty nextNodeProperty = propertyService.getNextNodeProperty(
                currentTask.getProcessDefinitionId(),
                currentTask.getTaskDefinitionKey(),
                processVariables);

        // 判断是否为驳回操作
        boolean isReject = DataDict.WfRejectStatus.REJECT.toString().equals(param.getIsReject());

        // 设置操作描述
        if (StringUtils.isBlank(param.getDescription())) {
            param.setDescription(isReject ? "驳回" : "同意");
        }

        // 确定下一节点处理人
        List<String> nextAssignees = determineNextAssignees(param, currentTask, nextNodeProperty, isReject);

        // 设置下一节点处理人到流程变量
        VariableUtil.setVariables(nextNodeProperty, processVariables, nextAssignees);

        // 添加任务评论
        if (StringUtils.isNotBlank(param.getDescription())) {
            taskService.addComment(currentTask.getId(), null, param.getDescription());
        }

        // 执行任务操作
        executeTaskOperation(currentTask, processVariables);

        // 准备返回结果
        WfResult result = new WfResult();
        result.setEnd(nextNodeProperty.isEnd());
        result.setProcInsId(processInstance.getId());

        // 准备返回结果中的任务信息
        prepareTaskInfoForResult(result, processInstance, isReject);

        // 保存下个节点的任务参数
        variableService.saveNextTaskVariablesLocal(getCurrentTasks(param.getProcInstId()), processVariables);

        // 保存流程实例和日志
        processInstanceService.save(processInstance);
        wfLogService.save(currentTask, processInstance.getStatus());

        // 设置返回结果中的任务ID和状态
        result.setTaskId(currentTask.getId());
        result.setStatus(processInstance.getStatus());
        result.setGroups(nextNodeProperty.getGroups());

        log.info("处理任务结束...");
        return result;
    }

    @Override
    public void delegateTask(CompleteParam model) {
        List<String> users = model.getSelectAssigneeList();
        if (users.size() == 0) {
            throw new BusinessException("当前任务不存在");
        }
        Task task = getTask(model.getProcInstId());
        // 将任务委托给某人
        taskService.delegateTask(task.getId(), users.get(0));
    }

    /**
     * 多个会签现在不支持退回
     * @param procInstId 实例编号
     */
    @Override
    public WfResult recallTask(String procInstId) {
        String taskDefKey = getLastTaskKey(procInstId);
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(procInstId).list();
        CommandExecutor commandExecutor = ((TaskServiceImpl)taskService).getCommandExecutor();
        commandExecutor.execute(new JumpAnyWhereCmd(tasks, taskDefKey));

        WfResult resultVo = new WfResult();
        resultVo.setEnd(false);
        resultVo.setProcInsId(procInstId);

        // 没有任务默认结束
        List<Task> nextTasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        // 多条线时，需要逗号隔开
        HashSet<String> set = nextTasks.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toCollection(HashSet::new));
        resultVo.setTaskDefKey(StringUtils.join(set, ","));
        //  节点名称
        set = nextTasks.stream().map(Task::getName).collect(Collectors.toCollection(HashSet::new));
        resultVo.setNodeName(StringUtils.join(set, ","));

        WfProcessInstance ins = processInstanceService.findById(procInstId);
        ins.setStatus(DataDict.WfInsStatus.BACK.toString());
        processInstanceService.save(ins);

        return resultVo;
    }

    private Task getTask(String instanceId) {
        List<Task> tasks = taskService.createTaskQuery()
                .includeProcessVariables()
                .includeTaskLocalVariables()
                .processInstanceId(instanceId).list();
        UserInfo userInfo = SessionUtil.getUserInfo();
        // 获取当前任务
        Boolean isAdmin = userInfo.validAdmin().getIsAdmin();
        if (isAdmin) {
            return tasks.get(0);
        }

        Optional<Task> optional = tasks.stream().filter(o -> o.getAssignee().equals(SessionUtil.getUser().getId())).findFirst();
        if (!optional.isPresent()) {
            throw new BusinessException("找不到任务数据");
        }

        return optional.get();
    }

    /**
     * 确定下一节点处理人
     */
    private List<String> determineNextAssignees(CompleteParam param, Task currentTask,
                                                NodeProperty nextNodeProperty, boolean isReject) {
        List<String> users = param.getSelectAssigneeList();

        if (!isReject && CollectionUtils.isEmpty(users) && !nextNodeProperty.isEnd()) {
            NodeSettingParam nodeSettingParam = new NodeSettingParam();
            nodeSettingParam.setTaskId(currentTask.getId());
            NodeSetting setting = propertyService.getNodeSetting(nodeSettingParam);
            users = setting.getNextUserList().stream()
                    .map(NodeUser::getId)
                    .collect(Collectors.toList());
        }
        return users;
    }

    /**
     * 执行任务操作
     */
    private void executeTaskOperation(Task task, Map<String, Object> variables) {
        DelegationState delegationState = task.getDelegationState();
        if (DelegationState.PENDING == delegationState) {
            taskService.resolveTask(task.getId(), variables);
        } else if (DelegationState.RESOLVED == delegationState) {
            taskService.complete(task.getId(), variables);
            wfLogService.save(task, DataDict.WfInsStatus.RUNNING.toString());
        } else {
            taskService.complete(task.getId(), variables);
        }
    }

    /**
     * 辅助方法：准备返回结果中的任务信息
     */
    private void prepareTaskInfoForResult(WfResult result, WfProcessInstance processInstance, boolean isReject) {
        List<Task> tasks = getCurrentTasks(processInstance.getProcInstId());

        if (tasks.isEmpty()) {
            processInstance.setStatus(DataDict.WfInsStatus.END.toString());
            result.setNodeName(result.getNodeName());
            result.setTaskId(result.getTaskDefKey());
        } else {
            Task firstTask = tasks.get(0);
            if (isReject) {
                Process process = propertyService.getMainProcess(firstTask.getProcessDefinitionId());
                if (WorkflowHelper.isStartNode(process, firstTask.getTaskDefinitionKey())) {
                    processInstance.setStatus(DataDict.WfInsStatus.RE_START.toString());
                } else {
                    processInstance.setStatus(DataDict.WfInsStatus.BACK.toString());
                }
            } else {
                processInstance.setStatus(DataDict.WfInsStatus.RUNNING.toString());
            }


            // 设置任务ID、名称和当前处理人
            Set<String> taskIds = tasks.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toSet());
            result.setTaskId(StringUtils.join(taskIds, ","));

            Set<String> taskNames = tasks.stream().map(Task::getName).collect(Collectors.toSet());
            result.setNodeName(StringUtils.join(taskNames, ","));

            Set<String> assignees = tasks.stream().map(Task::getAssignee).collect(Collectors.toSet());
            result.setUserId(StringUtils.join(assignees, ","));
        }
    }

    /**
     * 辅助方法：获取当前任务列表
     */
    private List<Task> getCurrentTasks(String procInstId) {
        return taskService.createTaskQuery().processInstanceId(procInstId).list();
    }

    private WfProcessInstance setWfProcessInstance(StartParam param) {
        String businessId = param.getBusinessId();

        WfProcessInstance instance = new WfProcessInstance();
        instance.setBusinessId(businessId);
        WfProcessInstance query = processInstanceService.findByBusinessId(businessId);
        if (!ObjectUtils.isEmpty(query)) {
            instance.setId(query.getId());
        }
        String startUserId = SessionUtil.getUser().getId();
        instance.setStartUserId(startUserId);
        String startDeptId = SessionUtil.getDefaultDeptId();
        instance.setStartDeptId(startDeptId);

        return instance;
    }

    private String getLastTaskKey(String procInstId) {
        String taskDefId = StringUtils.EMPTY;
        // 倒序获取数据
        List<WfLog> logs = wfLogService.findByProcInsId(procInstId);
        // 取最后一次审批节点的taskDefKey
        if (StringUtils.isBlank(taskDefId) && logs.size() > 0) {
            taskDefId = logs.get(0).getTaskDefKey();
        }

        return taskDefId;
    }
}
